package com.example.mediademo.decode.extractor;

import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import com.example.mediademo.Configs;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * 音视频数据提取器(提取视频或者音频中的信息和数据流，例如把视频文件剥离出音频和视频)
 */
public class MyExtractor {

    private MediaExtractor mediaExtractor;

    /**音频通道索引*/
    private int mAudioTrack = -1;

    /**视频通道索引*/
    private int mVideoTrack = -1;

    /**当前帧时间戳*/
    private long mCurSampleTime = 0;

    /**开始解码时间点*/
    private long mStartPos = 0;

    /**当前帧标志*/
    private int mCurSampleFlag = 0;

    // [1.配置数据来源]

    /**
     * 给MediaExtractor配置数据来源
     * @param filePath 媒体文件路径
     */
    public MyExtractor(String filePath) {
        try {
            mediaExtractor = new MediaExtractor();
            mediaExtractor.setDataSource(filePath);
        } catch (IOException e) {
            Log.e(Configs.TAG, "读取" + filePath + "文件出错:" + e.toString());
        }
    }

    //[2.获取音视频多媒体格式]
    // 便利遍历文件的所有通道，判断是否包含"video/"或者"audio/"开头的编码格式
    // 最后通过对应的通道索引，获取多媒体格式

    public MediaFormat getVideoFormat() {
        for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
            MediaFormat mediaFormat = mediaExtractor.getTrackFormat(i);
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/")) {
                mVideoTrack = i;
                break;
            }
        }
        return mVideoTrack >= 0 ? mediaExtractor.getTrackFormat(mVideoTrack) : null;
    }

    /**
     * 获取音频格式
     * @return MediaFormat
     */
    public MediaFormat getAudioFormat() {
        for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
            MediaFormat mediaFormat = mediaExtractor.getTrackFormat(i);
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                mAudioTrack = i;
                break;
            }
        }
        return mAudioTrack >= 0 ? mediaExtractor.getTrackFormat(mAudioTrack) : null;
    }

    //[3.提取数据]

    public int readBuffer(ByteBuffer byteBuffer) {
        // 清空存放待解码数据的缓冲区
        byteBuffer.clear();
        // 根据当前选择的通道，并切换成正确的通道
        selectSourceTrack();
        // 读取数据到byteBuffer，并返回数据大小
        int readSampleCount = mediaExtractor.readSampleData(byteBuffer,0);
        // count小于0，代表数据已经读完了
        if (readSampleCount < 0) {
            return -1;
        }
        // 保存当前帧的时间戳
        mCurSampleTime = mediaExtractor.getSampleTime();
        mCurSampleFlag = mediaExtractor.getSampleFlags();
        // 进入下一帧，调用advance把读取数据的指针移动到下一帧的开头
        mediaExtractor.advance();
        return readSampleCount;
    }

    /**
     * 选择通道
     */
    private void selectSourceTrack() {
        if (mVideoTrack >= 0) {
            mediaExtractor.selectTrack(mVideoTrack);
        } else if (mAudioTrack >= 0) {
            mediaExtractor.selectTrack(mAudioTrack);
        }
    }

    // 说明：seek(long pos)方法，主要用于跳播，快速将数据定位到指定的播放位置，
    // 但是，由于视频中，除了I帧以外，PB帧都需要依赖其他的帧进行解码，所以，通常只能seek到I帧，
    // 但是I帧通常和指定的播放位置有一定误差，因此需要指定seek靠近哪个关键帧，有以下三种类型：
    // SEEK_TO_PREVIOUS_SYNC：跳播位置的上一个关键帧
    // SEEK_TO_NEXT_SYNC：跳播位置的下一个关键帧
    // SEEK_TO_CLOSEST_SYNC：距离跳播位置的最近的关键帧

    /**
     * Seek到指定位置，并返回实际帧的时间戳
     * @param pos 位置
     * @return 时间戳
     */
    public long seek(long pos) {
        if (mediaExtractor != null) {
            mediaExtractor.seekTo(pos, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
            return mediaExtractor.getSampleTime();
        }
        return 0;
    }

    //[释放提取器]

    /**
     * 停止读取数据
     */
    public void stop() {
        mediaExtractor.release();
        mediaExtractor = null;
    }

    public int getmVideoTrack() {
        return mVideoTrack;
    }

    public int getmAudioTrack() {
        return mAudioTrack;
    }

    public void setStartPos(long pos) {
        mStartPos = pos;
    }

    public long getCurrentTimeStamp() {
        return mCurSampleTime;
    }

    public int getSampleFlag() {
        return mCurSampleFlag;
    }

}
